<template>
<div>
  <div v-for="(item, index) in tree" :key="item.treeNodeId" >
    <div v-show="item.inLimit" v-if="!item.children" class="item" style="margin-left: 15px" @click="clickItem(item)">
      <input v-if="showCheckbox" :disabled="item.disable" type="radio" :checked="item.checked" @click="changeCheck(item)"/> <span>{{item.label}}</span>
    </div>
    <div v-else>
        <div v-show="item.inLimit"  v-if="!item.expand" class="item">
            <div @click="toExpand(index),clickItem(item)" ><span>→</span><input v-if="showCheckbox" :disabled="item.disable" type="radio" :checked="item.checked" @click.stop="changeCheck(item)"/><span>{{item.label}}</span></div>
        </div>
        <div v-else>
            <div  v-show="item.inLimit" class="item" @click="toExpand(index),clickItem(item)"><span>↓</span><input v-if="showCheckbox" :disabled="item.disable" type="radio" :checked="item.checked" @click.stop="changeCheck(item)"/><span>{{item.label}}</span></div>
            <nuxt-tree :show-checkbox="showCheckbox" :tree-data="item.children" :tree-index="index" style="margin-left: 20px"></nuxt-tree>
        </div>
    </div>
  </div>
</div>
</template>

<script>
export default {
    name: 'NuxtTree',
    props: {
        'treeData': {
            type: Array,
            default: () => []
        },
        'showCheckbox': {
            type: Boolean,
            default: () => false
        },
        'treeIndex': {
            type: Number,
            default: -1
        },
        'limitData': {
            type: String,
            default: ''
        }
    },
    data() {
        return {
            /**
             * @type {Array}
             */
            tree: this.treeData
        }
    },
    watch: {
        limitData(newData) {
            function isInLimit(node, str) {
                let bool = false
                node.expand = true
                node.inLimit = node.label.includes(str)
                node.children && node.children.forEach(item => {
                    item.inLimit = isInLimit(item, newData)
                    bool = item.inLimit || bool
                })
                return node.inLimit || bool
            }
            this.tree.forEach(item => {
                isInLimit(item, newData)
            })    
        }
    },
    beforeMount() {
        if(this.$parent._name !== '<NuxtTree>') {
            // 使用栈来避免递归带来的性能损耗
            const stack = [this.tree]
            while(stack.length) {
                const arr = stack.pop()
                arr.forEach(item => {
                // 新增属性需要使用$set来使得属性为响应式
                this.$set(item, 'expand', item.expand ? item.expand : false)
                this.$set(item, 'treeNodeId', Math.floor(Math.random()*10000))
                this.$set(item, 'inLimit', true)
                if(this.showCheckbox) {
                    this.$set(item, 'checked', false)
                    this.$set(item, 'checkArr', [])
                }
                if(item.children) stack.push(item.children)
            })
            }
        }
    },
    methods: {
        toExpand(index) {
            this.tree[index].expand = !this.tree[index].expand
        },
        clickItem(item) {
            this.$parent._name === '<NuxtTree>' ? this.$parent.clickItem(item) :
            this.$emit('item-click', item)
        },
        changeCheck(item, effectDown = true) {
            item.checked = !item.checked
            if(!item.checked) {
                item.checkArr = []
                // 向下传播check影响
                if(item.children && effectDown) {
                    const stack = [item.children]
                    while(stack.length) {
                        const arr = stack.pop()
                        arr.forEach(value => {
                            value.checked = false
                            value.checkArr =  []
                            if(value.children) stack.push(value.children)
                        })
                    }
                }
                if(this.treeIndex !== -1) {
                    this.$parent._data.tree[this.treeIndex].checkArr.pop(item.treeNodeId)
                    if(this.$parent._data.tree[this.treeIndex].checkArr.length === 0) {
                        // 禁止对父亲的影响向下传播
                        this.$parent.changeCheck(this.$parent._data.tree[this.treeIndex], false)
                    }
                }
            }
            else {
                // 向下
                if(item.children && effectDown) {
                    item.checkArr = item.children.map(el => el.treeNodeId)
                    const stack = [item.children]
                    while(stack.length) {
                        const arr = stack.pop()
                        arr.forEach(value => {
                            value.checked = true
                            value.checkArr =  value.children ? value.children.map(el => el.treeNodeId) : []
                            if(value.children) stack.push(value.children)
                        })
                    }
                }
                if(this.treeIndex !== -1) {
                    this.$parent._data.tree[this.treeIndex].checkArr.push(item.treeNodeId)
                    if(this.$parent._data.tree[this.treeIndex].checkArr.length === 1) {
                        // 禁止对父亲的影响向下传播
                        this.$parent.changeCheck(this.$parent._data.tree[this.treeIndex], false)
                    }
                }
            }
        },
        getCheckId() {
            const stack = [this.tree]
            const checkeds = []
            while(stack.length !== 0) {
                const arr = stack.pop()
                arr.forEach(value => {
                    if(value.checked) checkeds.push(value.treeNodeId)
                    if(value.children) stack.push(value.children)
                })
            }
            return checkeds
        }
    }
}
</script>

<style>
    .item {
        cursor: pointer;
    }
    .item:hover {
        background: #99999930;
    }
</style>